package checker

import (
	"kumachan/standalone/rpc"
	"kumachan/standalone/rpc/vd"
	"kumachan/interpreter/lang/textual/ast"
	"kumachan/interpreter/lang/common/name"
	"kumachan/interpreter/lang/common/attr"
	"kumachan/interpreter/lang/common/source"
	"kumachan/interpreter/compiler/checker/typsys"
)


type RpcInfo struct {
	vd.SchemaTable
	rpc.ServiceIndex
	VdTypeInfoMapping
	ServiceInfoMapping
	VdTypeRecordDefaults
	VdUtilFunctionsInfo
}
type VdTypeInfoMapping (map[*typsys.TypeDef] VdTypeInfo)
type ServiceInfoMapping (map[*typsys.TypeDef] ServiceInfo)
type VdTypeInfo struct {
	Id    vd.TypeId
	Type  *vd.Type
}
type ServiceInfo struct {
	Identifier   rpc.ServiceIdentifier
	CtorArgType  *typsys.TypeDef
	MethodIndex  map[string] uint
}
type VdTypeRecordDefaults (map[vd.TypeId] (map[uint] name.FunctionName))
type VdUtilFunctionsInfo struct {
	VdValidatorIndex
	VdAdapterIndex
}
type VdValidatorIndex (map[vd.ValidatorId] name.FunctionName)
type VdAdapterIndex (map[vd.AdapterId] name.FunctionName)

func generateRpcInfo (
	mic    ModuleInfoCollection,
	types  typeList,
	t_reg  TypeRegistry,
	f_reg  *FunctionRegistry,
) (*RpcInfo, source.Errors) {
	var declared_vd_types, declared_srv_types =
		(func() (vd ([] TypeDef), srv ([] TypeDef)) {
			for _, def := range types {
				if def.DataConfig != (attr.TypeDataConfig {}) {
					vd = append(vd, def)
				}
				if def.ServiceConfig != (attr.TypeServiceConfig {}) {
					srv = append(srv, def)
				}
			}
			return
		})()
	var sch, tim, err1 = makeSchemaTable(declared_vd_types, mic)
	if err1 != nil { return nil, err1 }
	var srv, sim, err2 = makeServiceIndex(declared_srv_types, t_reg, tim, mic)
	if err2 != nil { return nil, err2 }
	var trd, err3 = makeVdTypeRecordDefaults(tim)
	if err3 != nil { return nil, err3 }
	var utl, err4 = makeVdUtilFunctionsInfo(*f_reg, t_reg, tim)
	if err4 != nil { return nil, err4 }
	injectVdApiFunctions(f_reg, t_reg, tim)
	injectRpcApiFunctions(f_reg, t_reg, sim)
	return &RpcInfo {
		SchemaTable:           sch,
		ServiceIndex:          srv,
		VdTypeInfoMapping:    tim,
		ServiceInfoMapping:    sim,
		VdTypeRecordDefaults: trd,
		VdUtilFunctionsInfo:  utl,
	}, nil
}

func injectVdApiFunctions(f_reg *FunctionRegistry, t_reg TypeRegistry, tim VdTypeInfoMapping) {
	for def, info := range tim {
		var type_name = def.Name
		var mod = type_name.ModuleName
		var item = type_name.ItemName
		var s_base_name = name.MakeName(mod, (item + F_Suffix_Serialize))
		var d_base_name = name.MakeName(mod, (item + F_Suffix_Deserialize))
		var tag = name.FunctionNameTagGenerated("vd", "api")
		var s_func_name = name.FunctionName { Name: s_base_name, Tag: tag }
		var d_func_name = name.FunctionName { Name: d_base_name, Tag: tag }
		var def_t = defType(def)
		var bytes_t = coreBytes(t_reg)
		var error_t = coreError(t_reg)
		var never_t = typsys.BottomType {}
		var s_sig = FunctionSignature {
			InputOutput: typsys.Lambda {
				Input:  def_t,
				Output: coreAsync(bytes_t, never_t)(t_reg),
			},
		}
		var d_sig = FunctionSignature {
			InputOutput: typsys.Lambda {
				Input:  bytes_t,
				Output: coreAsync(def_t, error_t)(t_reg),
			},
		}
		var s_body = SerializerBody { Type: info.Type }
		var d_body = DeserializerBody { Type: info.Type }
		var loc = def.Location
		var kind = ast.FK_Ordinary
		var s = craftFunction(s_func_name, kind, true, loc, s_sig, s_body, nil)
		var d = craftFunction(d_func_name, kind, true, loc, d_sig, d_body, nil)
		f_reg.InjectFunction(s)
		f_reg.InjectFunction(d)
	}
}
func injectRpcApiFunctions(f_reg *FunctionRegistry, t_reg TypeRegistry, sim ServiceInfoMapping) {
	for def, info := range sim {
		var type_name = def.Name
		var base_name = type_name.Name
		var service_tag = name.FunctionNameTagGenerated("rpc", "service")
		var f_name = name.FunctionName {
			Name: base_name,
			Tag:  service_tag,
		}
		var srv_type = defType(def)
		var ctor_arg_type = defType(info.CtorArgType)
		var id_type = rpcServiceIdentifier(srv_type, ctor_arg_type)
		var sig = FunctionSignature {
			InputOutput: typsys.Lambda {
				Input:  typsys.UnitType {},
				Output: id_type(t_reg),
			},
		}
		var body = ServiceIdentifierBody {
			Id: info.Identifier,
		}
		var loc = def.Location
		var kind = ast.FK_Constant
		var service_id = craftFunction(f_name, kind, true, loc, sig, body, nil)
		f_reg.InjectFunction(service_id)
	}
}

var vdNamedPrimitiveTypes = map[name.TypeName] vd.TypeKind {
	coreBool_name:          vd.Bool,
	coreInteger_name:       vd.Integer,
	coreNormalFloat_name:   vd.Float,
	coreNormalComplex_name: vd.Complex,
	coreString_name:        vd.String,
	coreBytes_name:         vd.Binary,
}
func vdGetType(t typsys.Type, mapping VdTypeInfoMapping, loc source.Location) (*vd.Type, *source.Error) {
	// 1. unit
	{ if getUnit(t) {
		return vd.PrimitiveType(vd.Unit), nil
	} }
	// 2. optional
	{ var inner_t, ok = coreMaybe_(t)
	if ok {
		var inner_vd, err = vdGetType(inner_t, mapping, loc)
		if err != nil { return nil, err }
		return vd.ContainerType(vd.Optional, inner_vd), nil
	} }
	// 3. array
	{ var item_t, ok = coreList_(t)
	if ok {
		var item_vd, err = vdGetType(item_t, mapping, loc)
		if err != nil { return nil, err }
		return vd.ContainerType(vd.Array, item_vd), nil
	} }
	// 4. others
	{ var def, type_args, ok = typeGetRefDefArgs(t)
	if ok && len(type_args) == 0 {
		// 4.1. serializable algebraic types
		{ var info, is_algebraic = mapping[def]
		if is_algebraic {
			return info.Type, nil
		} }
		// 4.2. named primitive types (unit not included)
		{ var primitive, is_primitive = vdNamedPrimitiveTypes[def.Name]
		if is_primitive {
			return vd.PrimitiveType(primitive), nil
		} }
	} }
	return nil, source.MakeError(loc,
		E_NotSerializable {
			TypeName: typsys.DescribeType(t, nil),
		})
}


